package com.yugao.fintech.draper.core.util;

import cn.hutool.core.util.ObjectUtil;
import com.yugao.fintech.draper.core.model.ErrorCode;
import com.yugao.fintech.draper.core.model.ResultVO;

import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class ResultConsumer<T> {

    /**
     * 一个空的{@code ResultOpt}
     */
    private static final ResultConsumer<?> EMPTY = new ResultConsumer<>(null);

    /**
     * 包裹的实际的元素
     */
    private final ResultVO<T> result;

    /**
     * 构造方法
     *
     * @param result 包裹里的元素
     */
    private ResultConsumer(ResultVO<T> result) {
        this.result = result;
    }

    /**
     * 返回一个包裹里元素不可能为空的{@code ResultOpt}
     *
     * @param result 包裹里的元素
     * @param <T>    包裹里元素的类型
     * @return 一个包裹里元素不可能为空的 {@code ResultOpt}
     * @throws java.lang.NullPointerException 如果传入的元素为空，抛出 {@code NPE}
     */
    public static <T> ResultConsumer<T> of(ResultVO<T> result) {
        return new ResultConsumer<>(Objects.requireNonNull(result));
    }

    /**
     * 返回一个包裹里元素可能为空的{@code ResultOpt}
     *
     * @param result 传入需要包裹的元素
     * @param <T>    包裹里元素的类型
     * @return 一个包裹里元素可能为空的 {@code ResultOpt}
     */
    public static <T> ResultConsumer<T> ofNullable(ResultVO<T> result) {
        return result == null ? empty() : new ResultConsumer<>(result);
    }

    /**
     * 返回一个空的{@code ResultOpt}
     *
     * @param <T> 包裹里元素的类型
     * @return Opp
     */
    public static <T> ResultConsumer<T> empty() {
        @SuppressWarnings("unchecked") final ResultConsumer<T> t = (ResultConsumer<T>) EMPTY;
        return t;
    }

    /**
     * 如果包裹里元素的值存在，就执行对应的操作，并返回本身 如果不存在，返回null{@code null}
     *
     * @param action 值存在时执行的操作
     * @return result
     * @throws java.lang.NullPointerException 如果action不存在
     */
    public ResultVO<T> peek(Consumer<ResultVO<T>> action) throws NullPointerException {
        Objects.requireNonNull(action);
        if (!isEmpty()) {
            action.accept(result);
        }

        return peek();
    }

    /**
     * 获取result
     *
     * @return result
     */
    public ResultVO<T> peek() {
        return result;
    }

    /**
     * 读取{@code code}的值
     *
     * @return 返回code的值
     */
    public Integer getCode() {
        return result.getCode();
    }

    /**
     * 读取{@code data}的值
     *
     * @return 返回 Optional 包装的data
     */
    public Optional<T> getData() {
        return Optional.ofNullable(result.getData());
    }

    /**
     * 读取{@code msg}的值
     *
     * @return 返回Optional包装的 msg
     */
    public Optional<String> getMsg() {
        return Optional.ofNullable(result.getMsg());
    }

    /**
     * 有条件地读取{@code data}的值
     *
     * @param predicate 断言函数
     * @return 返回 Optional 包装的data,如果断言失败返回empty
     */
    public Optional<T> filter(Predicate<? super ResultVO<?>> predicate) {
        return predicate.test(result) ? getData() : Optional.empty();
    }

    /**
     * 判断包裹里元素的值是否不存在，不存在为 {@code true}，否则为{@code false}
     *
     * @return 包裹里元素的值不存在 则为 {@code true}，否则为{@code false}
     */
    public boolean isEmpty() {
        return Objects.isNull(result);
    }

    /**
     * 对{@code code}的值进行相等性测试
     *
     * @param code 基准值
     * @return 返回ture表示相等
     */
    public boolean codeEquals(Integer code) {
        return !isEmpty() && result.getCode().equals(code);
    }

    /**
     * 对{@code code}的值进行相等性测试
     *
     * @param code 基准值
     * @return 返回ture表示不相等
     */
    public boolean codeNotEquals(Integer code) {
        return !codeEquals(code);
    }

    /**
     * 是否成功
     *
     * @return 返回ture表示成功
     */
    public boolean isSuccess() {
        return codeEquals(ErrorCode.SUCCESS.getCode());
    }

    /**
     * 是否失败
     *
     * @return 返回ture表示失败
     */
    public boolean notSuccess() {
        return !isSuccess();
    }

    /**
     * 断言{@code code}的值
     *
     * @param code 预期的值
     * @param func 用户函数,负责创建异常对象
     * @param <Ex> 异常类型
     * @return 返回实例，以便于继续进行链式操作
     * @throws Ex 断言失败时抛出
     */
    public <Ex extends Exception> ResultConsumer<T> assertCode(Integer code, Function<? super ResultVO<T>, ? extends Ex> func)
            throws Ex {
        if (codeNotEquals(code)) {
            throw func.apply(result);
        }

        return this;
    }

    /**
     * 断言成功
     *
     * @param func 用户函数,负责创建异常对象
     * @param <Ex> 异常类型
     * @return 返回实例，以便于继续进行链式操作
     * @throws Ex 断言失败时抛出
     */
    public <Ex extends Exception> ResultConsumer<T> assertSuccess(Function<? super ResultVO<T>, ? extends Ex> func) throws Ex {
        return assertCode(ErrorCode.SUCCESS.getCode(), func);
    }

    /**
     * 断言业务数据有值
     *
     * @param func 用户函数,负责创建异常对象
     * @param <Ex> 异常类型
     * @return 返回实例，以便于继续进行链式操作
     * @throws Ex 断言失败时抛出
     */
    public <Ex extends Exception> ResultConsumer<T> assertDataNotNull(Function<? super ResultVO<T>, ? extends Ex> func)
            throws Ex {
        if (Objects.isNull(result.getData())) {
            throw func.apply(result);
        }

        return this;
    }

    /**
     * 断言业务数据有值,并且包含元素
     *
     * @param func 用户函数,负责创建异常对象
     * @param <Ex> 异常类型
     * @return 返回实例，以便于继续进行链式操作
     * @throws Ex 断言失败时抛出
     */
    public <Ex extends Exception> ResultConsumer<T> assertDataNotEmpty(Function<? super ResultVO<T>, ? extends Ex> func)
            throws Ex {
        if (ObjectUtil.isEmpty(result.getData())) {
            throw func.apply(result);
        }

        return this;
    }

    /**
     * 对业务数据(data)转换
     *
     * @param mapper 业务数据转换函数
     * @param <U>    数据类型
     * @return 返回新实例，以便于继续进行链式操作
     */
    public <U> ResultConsumer<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);

        if (isEmpty()) {
            return empty();
        } else {
            return of(ResultVO.result(result.getCode(), result.getMsg(), mapper.apply(result.getData())));
        }
    }

    /**
     * 对业务数据(data)转换
     *
     * @param predicate 断言函数
     * @param mapper    业务数据转换函数
     * @param <U>       数据类型
     * @return 返回新实例
     */
    public <U> ResultConsumer<U> mapIf(Predicate<? super ResultVO<T>> predicate, Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(predicate);
        if (predicate.test(result)) {
            return empty();
        } else {
            return of(ResultVO.result(result.getCode(), result.getMsg(), mapper.apply(result.getData())));
        }
    }

    /**
     * 消费数据,注意此方法保证数据可用
     *
     * @param consumer 消费函数
     */
    public void consumerData(Consumer<? super T> consumer) {
        consumer.accept(result.getData());
    }

    /**
     * 条件消费(期望code匹配某个值)
     *
     * @param consumer 消费函数
     * @param codes    期望code集合 匹配任意一个则调用消费函数
     */
    public void consumerDataOnCode(Consumer<? super T> consumer, String... codes) {
        consumerDataIf(o -> Arrays.stream(codes).anyMatch(c -> result.getCode().equals(c)), consumer);
    }

    /**
     * 条件消费
     *
     * @param consumer 消费函数
     */
    public void consumerDataIfSuccess(Consumer<? super T> consumer) {
        Predicate<ResultVO<T>> success = r -> ErrorCode.SUCCESS.getCode().equals(r.getCode());
        consumerDataIf(success, consumer);
    }

    /**
     * 条件消费
     *
     * @param predicate 断言函数
     * @param consumer  消费函数,断言函数返回{@code true}时被调用
     */
    public void consumerDataIf(Predicate<? super ResultVO<T>> predicate, Consumer<? super T> consumer) {
        if (predicate.test(result)) {
            consumer.accept(result.getData());
        }
    }
}
